package jp.seraph.jsade.perceptor;

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.math.geometry.Vector3D;

/**
 * Seeパーセプタによって受信する，エージェントの視覚情報
 */
public class SeeObject {
    public SeeObject(SeeObjectType aType, SeeObjectConfidence aConfidence, String aTeamName, int aId, Map<String, SeePartObject> aParts) {
        init(aType, aConfidence, aTeamName, aId, aParts);
    }

    public SeeObject(SeeObjectType aType, SeeObjectConfidence aConfidence, String aTeamName, int aID, double aDistance, double aHorizontalAngle, double aLatitudalAngle) {
        SeePartObject tPart = new SeePartObject(aType.getTypeName(), aDistance, aHorizontalAngle, aLatitudalAngle);

        Map<String, SeePartObject> tParts = new HashMap<String, SeePartObject>();
        tParts.put(tPart.getName(), tPart);

        init(aType, aConfidence, aTeamName, aID, tParts);
    }

    public SeeObject(SeeObjectType aType, SeeObjectConfidence aConfidence, String aTeamName, int aID, Vector3D aVector) {
        SeePartObject tPart = new SeePartObject(aType.getTypeName(), aVector);

        Map<String, SeePartObject> tParts = new HashMap<String, SeePartObject>();
        tParts.put(tPart.getName(), tPart);

        init(aType, aConfidence, aTeamName, aID, tParts);
    }

    public SeeObject(SeeObjectType aType, SeeObjectConfidence aConfidence, double aDistance, double aHorizontalAngle, double aLatitudalAngle) {
        this(aType, aConfidence, "", -1, aDistance, aHorizontalAngle, aLatitudalAngle);
    }

    public SeeObject(SeeObjectType aType, SeeObjectConfidence aConfidence, Vector3D aVector){
        this(aType, aConfidence, "", -1, aVector);
    }

    private void init(SeeObjectType aType, SeeObjectConfidence aConfidence, String aTeamName, int aId, Map<String, SeePartObject> aParts) {
        mType = aType;
        mConfidence = aConfidence;

        if (mType == SeeObjectType.PLAYER) {
            mTeamName = aTeamName;
            mID = aId;
        } else {
            mTeamName = "";
            mID = -1;
        }

        mParts = aParts;
    }

    private SeeObjectType mType;

    private SeeObjectConfidence mConfidence;

    private String mTeamName;

    private int mID;

    private Map<String, SeePartObject> mParts;

    /**
     * このSeeObjectが表す物の種類を取得する。
     *
     * @return
     */
    public SeeObjectType getType() {
        return mType;
    }

    /**
     * このSeeObjectの信頼性を表わす列挙の値を返します。
     * この値がUnpredictableであるとき、他のこのクラスのgetメソッドの返り値は不定です。
     *
     * @return
     */
    public SeeObjectConfidence getConfidence(){
        return mConfidence;
    }

    /**
     * getType()がSeeObjectType.PLAYERである場合にはこのオブジェクトの所属チーム名を返す。 それ以外の場合空文字列を返す。
     *
     * @return
     */
    public String getTeamName() {
        return mTeamName;
    }

    /**
     * getType()がSeeObjectType.PLAYERである場合には、このオブジェクトのIDを返す。 それ以外場合-1を返す。
     *
     * @return
     */
    public int getID() {
        return mID;
    }

    /**
     * 主観とこのオブジェクトの距離を返す。
     *
     * @return
     */
    public double getDistance() {
        double tResult = 0.0;
        int tCount = 0;
        for (SeePartObject tPart : mParts.values()) {
            tResult += tPart.getVecotr().getNorm();
            tCount++;
        }

        return tResult / tCount;
    }

    /**
     * 主観とこのオブジェクトの相対水平方向角度[°]を返す。 前方が0° 左回転方向が正である。
     *
     * @return
     */
    public double getHorizontalAngle() {
        double tResult = 0.0;
        int tCount = 0;
        for (SeePartObject tPart : mParts.values()) {
            tResult += tPart.getHorizontalAngle();
            tCount++;
        }

        return tResult / tCount;
    }

    /**
     * 主観とこのオブジェクトの相対垂直角度[°]を返す。 水平面方向が0° 上方が正である。
     *
     * @return
     */
    public double getLatitudalAngle() {
        double tResult = 0.0;
        int tCount = 0;
        for (SeePartObject tPart : mParts.values()) {
            tResult += tPart.getLatitudalAngle();
            tCount++;
        }

        return tResult / tCount;
    }

    /**
     * 主観からこのオブジェクトへのベクトルを返します。 x方向が前方 y方向が左方 z方向が上方 （のはず）
     *
     * @return
     */
    public Vector3D getVector() {
        Vector3D tResult = Vector3D.zero;
        double tCount = 0;
        for (SeePartObject tPart : mParts.values()) {
            tResult = tResult.add(tPart.getVecotr());
            tCount += 1;
        }

        return tResult.scalarMultiply(1.0 / tCount);
    }
}
